package main

import "fmt"

//结构体类型AnimalCategory代表了动物的基本分类法，
//其中有 7 个string类型的字段，分别表示各个等级的分类。
type AnimalCategory struct {
	kingdom string // 界。
	phylum  string // 门。
	class   string // 纲。
	order   string // 目。
	family  string // 科。
	genus   string // 属。
	species string // 种。
}

//隶属于AnimalCategory类型的方法。通过该方法的接收者名称ac，
//我们可以在其中引用到当前值的任何一个字段，或者调用到当前值的任何一个方法（也包括String方法自己）。

//在Go语言中，我们可以通过为一个类型编写名为String的方法，来自定义该类型的字符串表示形式。
//这个String方法不需要任何参数声明，但需要有一个string类型的结果声明。正因为如此，我在调用fmt.Printf函数时，
//使用占位符%s和category值本身就可以打印出后者的字符串表示形式，而无需显式地调用它的String方法。
//一个数据类型关联的所有方法，共同组成了该类型的方法集合。
func (ac AnimalCategory) String() string {
	fmt.Println("调用AnimalCategory的string方法")
	return fmt.Sprintf("%s%s%s%s%s%s%s", ac.kingdom, ac.phylum, ac.class, ac.order, ac.family, ac.genus, ac.species)
}

// 示例2。
type Animal struct {
	scientificName string // 学名。
	AnimalCategory        // 动物基本分类。
}

// 该方法会"屏蔽"掉嵌入字段中的同名方法。
func (a Animal) String() string {
	return fmt.Sprintf("%s (category: %s)", a.scientificName, a.AnimalCategory)
}

// 示例3。
type Cat struct {
	name string
	Animal
}

// 该方法会"屏蔽"掉嵌入字段中的同名方法。
// 结构体类型Cat中有一个嵌入字段Animal，而Animal类型还有一个嵌入字段AnimalCategory。
// 在这种情况下，“屏蔽”现象会以嵌入的层级为依据，嵌入层级越深的字段或方法越可能被“屏蔽”。
// 例如，当我们调用Cat类型值的String方法时，如果该类型确有String方法，那么嵌入字段Animal和AnimalCategory的String方法都会被“屏蔽”。
// 如果该类型没有String方法，那么嵌入字段Animal的String方法会被调用，而它的嵌入字段AnimalCategory的String方法仍然会被屏蔽。
// 只有当Cat类型和Animal类型都没有String方法的时候，AnimalCategory的String方法菜会被调用。
func (cat Cat) String() string {
	return fmt.Sprintf("%s (category: %s, name: %q)", cat.scientificName, cat.Animal.AnimalCategory, cat.name)
}


func main() {

	category := AnimalCategory{species: "cat"}
	//调用string方法
	fmt.Printf("The animal category: %s\n", category)

	// 示例2。
	animal := Animal{
		scientificName: "American Shorthair",
		AnimalCategory: category,
	}
	fmt.Printf("The animal: %s\n", animal)

	// 示例3。
	cat := Cat{
		name:   "little pig",
		Animal: animal,
	}
	fmt.Printf("The cat: %s\n", cat)
}
